package xyz.cerberu.cet.shiro.filter;

import org.apache.shiro.mgt.AbstractRememberMeManager;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.SubjectContext;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import xyz.cerberu.cet.global.utils.RequestUtils;
import xyz.cerberu.cet.shiro.utils.TokenGenerator;
import xyz.cerberu.cet.user.domain.User;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author β世界
 * Created on 8:10
 * @Description 使用Redis来实现记住我操作
 */
@Component
public class RedisRememberMeManager extends AbstractRememberMeManager {

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Value("${global.remember-me-cookie-name}")
    private String cookieName = "rememberMe";

    @Value("${global.remember-me-max-age}")
    public int maxAge;

    private static final String PREFIX = "remember:";

    @Override
    protected void forgetIdentity(Subject subject) {
        forgetIdentity(WebUtils.getHttpRequest(subject),WebUtils.getHttpResponse(subject));
    }

    @Override
    protected void rememberSerializedIdentity(Subject subject, byte[] serialized) {
        // 序列化完成后，将一个token写入到http中，方便我们下次去取到数据
        HttpServletRequest request = WebUtils.getHttpRequest(subject);
        HttpServletResponse response = WebUtils.getHttpResponse(subject);

        // 封装信息
        String realm = subject.getPrincipals().getRealmNames().stream().iterator().next();
        User user = (User) subject.getPrincipals().getPrimaryPrincipal();

        HashMap<String,Object> hashMap = new HashMap<>();
        hashMap.put("user",user);
        hashMap.put("realm",realm);

        // 生成token，并且将这个token写入request中
        String token = TokenGenerator.generateValue(user.getUid());
        Cookie cookie = getCookie();
        cookie.setValue(token);

        // 将Subject中的Principals对象序列化即可，id就是我们上面生成的token，并且存活时间为cookie的存活时间
        redisTemplate.opsForValue().set(token,hashMap, maxAge, TimeUnit.SECONDS);
        // 将cookie返回给客户端
        cookie.saveTo(request,response);
    }

    @SuppressWarnings("unchecked")
    @Override
    protected byte[] getRememberedSerializedIdentity(SubjectContext subjectContext) {
        // 从请求中获取到我们的token
        HttpServletRequest request = WebUtils.getHttpRequest(subjectContext);

        // 如果这个cookie的名字和我们存入的cookie名字相同，那么我就获取到他的值，然后去读取
        javax.servlet.http.Cookie cookie = RequestUtils.getCookie(request,cookieName);
        if(cookie != null){
            // 将读取到的对象序列化成byte数组
           HashMap<String,Object> hashMap = (HashMap<String,Object>) redisTemplate.opsForValue().get(cookie.getValue());
            if(hashMap != null){
                SimplePrincipalCollection simplePrincipalCollection = new SimplePrincipalCollection(hashMap.get("user"), (String) hashMap.get("realm"));
                return convertPrincipalsToBytes(simplePrincipalCollection);
            }
        }

        // 如果遍历完成都没有，那就返回null
        return null;
    }

    @Override
    public void forgetIdentity(SubjectContext subjectContext) {
        forgetIdentity(WebUtils.getHttpRequest(subjectContext),WebUtils.getHttpResponse(subjectContext));
    }

    private void forgetIdentity(HttpServletRequest request, HttpServletResponse response){
        javax.servlet.http.Cookie cookie = RequestUtils.getCookie(request,cookieName);
        if(cookie != null){
            redisTemplate.delete(cookie.getValue());
        }
    }

    private Cookie getCookie(){
        Cookie cookie = new SimpleCookie(cookieName);
        cookie.setMaxAge(maxAge);
        cookie.setSameSite(Cookie.SameSiteOptions.LAX);
        cookie.setHttpOnly(true);
        return cookie;
    }
}
